#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <oci.h>

#include "dbcom.h"
#include "dbfunc.h"

static text *username="dbuser";
static text *password="oracle";
OCIEnv *p_henv;
thrd_conn_t a_thrd_conn[CONNECT_QT];


sb4 checkerr(OCIError *p_herr, sword status, sb4 *errcode)
{
    text errbuf[512];

    *errcode=0;
    switch(status)
    {
      case OCI_SUCCESS:
        break;

      case OCI_SUCCESS_WITH_INFO:
        printf("Error - OCI_SUCCESS_WITH_INFO\n");
        break;

      case OCI_NEED_DATA:
        printf("Error - OCI_NEED_DATA\n");
        break;

      case OCI_NO_DATA:
/*
        printf("Error - OCI_NODATA\n");
*/
        *errcode=DB_NOTFOUND;
        break;

      case OCI_ERROR:
        OCIErrorGet((dvoid *)p_herr, (ub4)1, (text *)NULL,
            errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
        printf("Error - %.*s\n", sizeof(errbuf), errbuf);
        printf("Error - OCI_ERROR\n");
        if(*errcode==0)  *errcode=DB_ERROR;
        break;

      case OCI_INVALID_HANDLE:
        printf("Error - OCI_INVALID_HANDLE\n");
        *errcode=DB_ERROR;
        break;

      case OCI_STILL_EXECUTING:
        printf("Error - OCI_STILL_EXECUTE\n");
        break;

      case OCI_CONTINUE:
        printf("Error - OCI_CONTINUE\n");
        break;

      default:
        break;
    }

    return(status);
}


int DbsInit()
{
    DefaultDefinition;
    int thrd_index, i;

    p_henv=(OCIEnv *)NULL;
    status=OCIEnvCreate(&p_henv,
        (ub4)(OCI_THREADED|OCI_OBJECT|OCI_SHARED),
        (dvoid *)NULL, (dvoid *(*)(dvoid *,size_t))NULL,
        (dvoid *(*)(dvoid *,dvoid *,size_t))NULL,
        (void (*)(dvoid *,dvoid *))NULL, (size_t)0, (dvoid **)NULL);
    if(status==OCI_ERROR)
    {
        p_henv=(OCIEnv *)NULL;
        return(DB_ERROR);
    }
    for(thrd_index=0; thrd_index<CONNECT_QT; thrd_index++)
    {
        ThrdConn.connected=0;
        ThrdConn.p_herr=(OCIError *)NULL;
        ThrdConn.p_hsvc=(OCISvcCtx *)NULL;
        ThrdConn.p_hsrv=(OCIServer *)NULL;
        ThrdConn.p_haut=(OCISession *)NULL;
        ThrdConn.p_hstmt=(OCIStmt *)NULL;
        for(i=0; i<HANDLER_BIND_QT; i++)
            ThrdConn.a_hbnd[i]=(OCIBind *)NULL;
        for(i=0; i<HANDLER_DEFINE_QT; i++)
            ThrdConn.a_hdef[i]=(OCIDefine *)NULL;
    }

    return(DB_OK);
}


int DbsConnect(int thrd_index)
{
    DefaultDefinition;

    ReturnErr(OCIHandleAlloc((CONST dvoid *)p_henv,
        (dvoid **)&ThrdConn.p_herr, (ub4)OCI_HTYPE_ERROR,
        (size_t)0, (dvoid **)NULL));

    ReturnErr(OCIHandleAlloc((CONST dvoid *)p_henv,
        (dvoid **)&ThrdConn.p_hsvc, (ub4)OCI_HTYPE_SVCCTX,
        (size_t)0, (dvoid **)NULL));
    ReturnErr(OCIHandleAlloc((CONST dvoid *)p_henv,
        (dvoid **)&ThrdConn.p_hsrv, (ub4)OCI_HTYPE_SERVER,
        (size_t)0, (dvoid **)NULL));
    OCIServerAttach(ThrdConn.p_hsrv, ThrdConn.p_herr,
        (CONST text *)NULL, (sb4)0, (ub4)0);
    OCIAttrSet((dvoid *)ThrdConn.p_hsvc, (ub4)OCI_HTYPE_SVCCTX,
        (dvoid *)ThrdConn.p_hsrv, (ub4)0, (ub4)OCI_ATTR_SERVER,
        ThrdConn.p_herr);

    ReturnErr(OCIHandleAlloc((CONST dvoid *)p_henv,
        (dvoid **)&ThrdConn.p_haut, (ub4)OCI_HTYPE_SESSION,
        (size_t)0, (dvoid **)NULL));
    OCIAttrSet((dvoid *)ThrdConn.p_haut, (ub4)OCI_HTYPE_SESSION,
        (dvoid *)username, (ub4)strlen((char *)username),
        (ub4)OCI_ATTR_USERNAME, ThrdConn.p_herr);
    OCIAttrSet((dvoid *)ThrdConn.p_haut, (ub4)OCI_HTYPE_SESSION,
        (dvoid *)password, (ub4)strlen((char *)password),
        (ub4)OCI_ATTR_PASSWORD, ThrdConn.p_herr);

    ReturnErr(OCISessionBegin(ThrdConn.p_hsvc, ThrdConn.p_herr,
        ThrdConn.p_haut, OCI_CRED_RDBMS, (ub4)OCI_DEFAULT));
    OCIAttrSet((dvoid *)ThrdConn.p_hsvc, (ub4)OCI_HTYPE_SVCCTX,
        (dvoid *)ThrdConn.p_haut, (ub4)0, (ub4)OCI_ATTR_SESSION,
        ThrdConn.p_herr);

    ReturnErr(OCIDescriptorAlloc((dvoid *)p_henv,
        (dvoid **)&ThrdConn.p_blob, (ub4) OCI_DTYPE_LOB,
        (size_t)0, (dvoid **)NULL));
    
    ThrdConn.connected=1;
    return(DB_OK);
  ERROR:
    return((int)errcode);
}


void DbsDisConnect(int thrd_index)
{
    if(ThrdConn.connected)
    {
        OCISessionEnd(ThrdConn.p_hsvc, ThrdConn.p_herr,
            ThrdConn.p_haut, OCI_DEFAULT);
        OCIServerDetach(ThrdConn.p_hsrv, ThrdConn.p_herr,
            OCI_DEFAULT);
        ThrdConn.connected=0;
    }
    return;
}


void DbsDisConnectAll()
{
    int thrd_index;

    for(thrd_index=0; thrd_index<CONNECT_QT; thrd_index++)
        DbsDisConnect(thrd_index);
    OCIHandleFree((dvoid *)p_henv, OCI_HTYPE_ENV);
}


void DbsCommit(int thrd_index)
{
    OCITransCommit(ThrdConn.p_hsvc, ThrdConn.p_herr,
        (ub4)OCI_DEFAULT);
}


void DbsRollback(int thrd_index)
{
    OCITransRollback(ThrdConn.p_hsvc, ThrdConn.p_herr,
        (ub4)OCI_DEFAULT);
}


int read_blob(int thrd_index, OCILobLocator *p_blob,
    char **p_read_buf, sb4 *p_size, sb4 *p_errcode)
{
    DefaultDefinition;
    ub4 amt, offset, remainder;
    dvoid *buf;

    if(CheckErr(OCILobGetLength(ThrdConn.p_hsvc, ThrdConn.p_herr,
           p_blob, p_size)))
    {   *p_errcode=errcode;  return(-1);   }
    if(*p_size<=0)
    {
        *p_read_buf=(char *)NULL;
        return(0);
    }
/*
    printf("read_blob:size=%d\n", *p_size);
*/

    if(!(buf=(dvoid *)malloc((*p_size)+10)))
    {   *p_errcode=DB_ERROR;  return(-1);   }
    remainder=*p_size;
    offset=1;
    while(1) 
    {
        amt=0;
        CheckErr(OCILobRead(ThrdConn.p_hsvc, ThrdConn.p_herr,
            p_blob, &amt, (ub4)1,
            buf+offset-1, (ub4)LOB_BUF_LEN, 
            (dvoid *)NULL,
            (sb4 (*)(dvoid *, const dvoid *, ub4, ub1))NULL,
            (ub2)0, (ub1) SQLCS_IMPLICIT));
/*
        printf("status:%d amt:%d\n", status, amt);
*/
        if(status!=OCI_SUCCESS && status!=OCI_NEED_DATA)
        {   free(buf);  *p_errcode=errcode;  return(-1);   }
        if(status==OCI_SUCCESS)  break;
        offset+=amt;
        remainder-=amt;
    }
    *p_read_buf=(char *)buf;
    (*p_read_buf)[*p_size]='\0';

    return(0);
}


int write_blob(int thrd_index, char *write_buf, sb4 size,
    OCILobLocator *p_blob, sb4 *p_errcode)
{
    DefaultDefinition;
    sb4 offset, amt, remainder;

    if(size<=0)  return(0);

    if(size>LOB_BUF_LEN)
    {
        offset=1;
        amt=LOB_BUF_LEN;
        remainder=size;
        if(CheckErr(OCILobWrite(ThrdConn.p_hsvc, ThrdConn.p_herr,
               p_blob, &amt, offset,
               (dvoid *)write_buf, remainder+10, OCI_FIRST_PIECE,
               (dvoid *)NULL, (sb4 (*)())NULL,
               (ub2)0, (ub1)SQLCS_IMPLICIT)))
        {   *p_errcode=errcode;  return(-1);   }
        remainder-=amt;
        offset+=amt;
        while(remainder>LOB_BUF_LEN)
        {
            amt=LOB_BUF_LEN;
            if(CheckErr(OCILobWrite(ThrdConn.p_hsvc, ThrdConn.p_herr,
                   p_blob, &amt, offset,
                   (dvoid *)write_buf, remainder+10, OCI_NEXT_PIECE,
                   (dvoid *)NULL, (sb4 (*)())NULL,
                   (ub2)0, (ub1) SQLCS_IMPLICIT)))
            {   *p_errcode=errcode;  return(-1);   }
            remainder-=amt;
            offset+=amt;
        }
        amt=remainder;
        if(CheckErr(OCILobWrite(ThrdConn.p_hsvc, ThrdConn.p_herr,
               p_blob, &amt, offset,
               (dvoid *)write_buf, remainder+10, OCI_LAST_PIECE,
               (dvoid *)NULL, (sb4 (*)())NULL,
               (ub2)0, (ub1) SQLCS_IMPLICIT)))
        {   *p_errcode=errcode;  return(-1);   }
    }
    else
    {
        amt=size;
        if(CheckErr(OCILobWrite(ThrdConn.p_hsvc, ThrdConn.p_herr,
               p_blob, &amt, (ub4)1,
               (dvoid *)write_buf, (ub4)(size+10), OCI_ONE_PIECE,
               (dvoid *)NULL, (sb4 (*)())NULL,
               (ub2)0, (ub1)SQLCS_IMPLICIT)))
        {   *p_errcode=errcode;  return(-1);   }
        printf("lob write size:%d amt:%d\n", size, amt);
    }

    return(0);
}


int DbsSQLExec(int thrd_index, char *sql)
{
    DefaultDefinition;

    StartFunc;

    strcpy(ThrdConn.sql, sql);
    ReturnErr(StmtPrepare);
    CheckErr(StmtExecute);
    if(status!=OCI_SUCCESS)
    {
        if(status==OCI_NO_DATA)  errcode=DB_NOTFOUND;
        goto ERROR;
    }

    EndFunc;
}
